package com.bw.test_fix;

import android.content.Context;

import java.lang.reflect.Array;
import java.lang.reflect.Field;

import dalvik.system.DexClassLoader;
import dalvik.system.PathClassLoader;

/**
 * @author ytx
 * @date 2025-10-30 15:49
 * @description
 */
public class FixManager {
    private Context mContext;
    private FixManager(Context context){
        mContext = context;
    }
    private static FixManager manager;
    public static FixManager getInstance(Context context){
        if(manager == null){
            synchronized (FixManager.class){
                if(manager == null){
                    manager = new FixManager(context);
                }
            }
        }
        return manager;
    }


    public void loadFixClass() throws NoSuchFieldException, IllegalAccessException {
        //1.反射机制获得补丁包的dexElements:DexClassLoader
        //1.0 准备补丁包的路径
        String patchPath = mContext.getExternalFilesDir(null).getAbsolutePath()+"/output.jar";//补丁包的SD卡路径，
        String cachePatchPath = mContext.getDir("patch",Context.MODE_PRIVATE).getAbsolutePath();//补丁包的缓存路径
        //1.1 将补丁到的dex文件加载到虚拟机内存中
        DexClassLoader dexClassLoader = new DexClassLoader(patchPath,cachePatchPath,null,mContext.getClassLoader());
        Class<?> superclass = dexClassLoader.getClass().getSuperclass();//获得BaseDexClassLoader class对象
        Field pathListField = superclass.getDeclaredField("pathList");//获得BaseDexClassLoader类成员属性
        pathListField.setAccessible(true);//属性的私有的需要暴力访问
        Object pathListObject = pathListField.get(dexClassLoader);//获得dexClassLoader对象的pathList属性值
        Field dexElementsField = pathListObject.getClass().getDeclaredField("dexElements");//获得PathList类的成员属性
        dexElementsField.setAccessible(true);//属性的私有的需要暴力访问
        Object dexElementsObject = dexElementsField.get(pathListObject);//获得pathListObject对象dexElements属性值
        //2.反射机制获得宿主app的 dexElements
        ClassLoader pathClassLoader = mContext.getClassLoader();//获得PathClassLoader加载器对象
        Object myPathListField = pathListField.get(pathClassLoader);//获得PathClassLoader对象的pathList属性值
        Object myDexElementsObject = dexElementsField.get(myPathListField);//获得myPathListField对象dexElements属性值
        //3.将2个数组合并newDexElements：补丁包在前面，宿主在后面
        int fixLength = Array.getLength(dexElementsObject);//补丁包的长度
        int myLength = Array.getLength(myDexElementsObject);//宿主数组长度
        int newDexElementsLength = fixLength + myLength;//新数组的长度
        //新的数组 参数一：数组中元素的类型 参数二：长度
        Object newDexElements = Array.newInstance(dexElementsObject.getClass().getComponentType(), newDexElementsLength);
        for(int i = 0;i<newDexElementsLength;i++){
            if(i<fixLength){//放补丁包
                Object o = Array.get(dexElementsObject, i);
                Array.set(newDexElements,i,o);
            }else{
                Object o = Array.get(myDexElementsObject, i - fixLength);
                Array.set(newDexElements,i,o);
            }
        }
        //4.反射机制将newDexElements新数组给宿主app放回去
        dexElementsField.set(myPathListField,newDexElements);
    }
}
